/**
  ETFDAQ Project
  \class TADAQThread
  \brief the working thread for the DAQ
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2024-08-20
  \date 2024-08-30 last modified

  \copyright Copyright (c) 2024 IMP-CAS with LGPLv3 LICENSE
*/

#include <iostream>
#include <unistd.h>
#include <QEventLoop>
#include "TADAQThread.h"
#include "TAGDAQ.h"
#include "TADAQ.h"
#include "TAMsg.h"

using std::cout;
using std::endl;
using std::flush;

#define er TAMsg::Error

TADAQThread::TADAQThread(TAGDAQ *gdaq, TADAQ *daq) : QThread((QObject *)gdaq),
fGDAQ(gdaq), fDaqSta(EDAQSTA::UDF), fDAQ(daq), fEvIdx(0){
  if(!daq) er("TADAQThread", "ctor: nullptr daq");
  if(!gdaq) er("TADAQThread", "ctor: nullptr gdaq");
  fEventLoop = new QEventLoop(this);
} // end ctor

TADAQThread::~TADAQThread(){
  delete fDAQ;
} // end dtor

void TADAQThread::ProcessOneEvent(){
  fDAQ->DropDead(); // set ch_out to forbit incoming triggers
  if(fDAQ->IsIRQed()){ // an IRQ has arrived
    fDAQ->ReadEvent(); // module buffer => controller buffer
    fDAQ->ClearData(); // clear the cards for the next event
  } // end if
} // end member function ProcessOneEvent

void TADAQThread::run(){
  time_t t0 = TAMsg::ms(); // the current time
  EDAQSTA sta = daqStatus();
  bool nex = true; // true: no exit; false: exit
  while(nex){
    sta = daqStatus();
    switch(sta){
      case EDAQSTA::INIT:
        fDAQ->Initialize();
        updateStatus(EDAQSTA::INITED);
        fDAQ->SetDaqStatus(EDAQSTA::INITED);
        fGDAQ->inited();
        break;
      case EDAQSTA::START:
        fDAQ->OpenFile(fGDAQ->startComment());
        updateDAQInfo();
        updateStatus(EDAQSTA::RUNNING);
        fDAQ->SetDaqStatus(EDAQSTA::RUNNING);
        fDAQ->ClearData();
        fDAQ->Resurrect(); // clear ch_out to allow triggers, and enable irq
        fGDAQ->unlock();
        break;
      case EDAQSTA::RESUME:
        ProcessOneEvent();
        fDAQ->Resurrect(); // clear ch_out to allow triggers, and enable irq
        updateStatus(EDAQSTA::RUNNING);
        fDAQ->SetDaqStatus(EDAQSTA::RUNNING);
        fGDAQ->unlock();
        break;
      case EDAQSTA::PAUSE:
        usleep(100000);
        ProcessOneEvent();
        updateStatus(EDAQSTA::PAUSED);
        fDAQ->SetDaqStatus(EDAQSTA::PAUSED);
        fGDAQ->unlock();
        break;
      case EDAQSTA::EXIT:
        nex = false;
        if(fDAQ->status() == EDAQSTA::RUNNING){
          ProcessOneEvent();
          fDAQ->CloseFile(fGDAQ->stopComment());
          fDAQ->SetDaqStatus(EDAQSTA::EXIT);
        } // end if
        break;
      case EDAQSTA::STOP:
        ProcessOneEvent();
        fDAQ->CloseFile(fGDAQ->stopComment());
        fGDAQ->prepareForANewRun();
        updateStatus(EDAQSTA::IDLE);
        fDAQ->SetDaqStatus(EDAQSTA::IDLE);
        fGDAQ->unlock();
        break;
      case EDAQSTA::RUNNING:
        if(fDAQ->IsIRQed()){ // an IRQ has arrived
          fDAQ->DropDead(); // set ch_out to forbit incoming triggers
          fDAQ->ReadEvent(); // module buffer => controller buffer
          fDAQ->ClearData(); // clear the cards for the next event
          fDAQ->Resurrect(); // clear ch_out to allow triggers, and enable irq
        } // end if
      case EDAQSTA::PAUSED:
        if(TAMsg::ms() - t0 > 1000){ // update DAQ info every second
          t0 = TAMsg::ms();
          updateDAQInfo();
        } // end if
        break;
      default: break;
    } // end switch
    fEventLoop->processEvents();
  } // end while
  exec(); // takes signals to exit
} // end member function run


void TADAQThread::updateStatus(const EDAQSTA &sta){
  QMutexLocker lock(&fMutex);
  fDaqSta = sta;
} // end membre function updateStatus

EDAQSTA TADAQThread::daqStatus(){
  QMutexLocker lock(&fMutex);
  return fDaqSta;
} // end member function daqStatus

void TADAQThread::updateDAQInfo(){
  char msg[512]{};
  sprintf(msg, "File Name: %s\nFile Size: %.2fMB \tEvent Length: %u words"
    "\nBlock Number: %u\t Event Number: %u\nDuration: %s\t DAQ Rate: %d/s\ntime: %s",
    fDAQ->GetFileName(), fDAQ->GetFileSizeInMB(), fDAQ->GetEvLen(),
    fDAQ->GetCntBlk(), fDAQ->GetCntEv(), TAMsg::secInterval(fDAQ->StartTime()),
    fDAQ->GetCntEv() - fEvIdx, TAMsg::sec0());
  fDAQInfo = msg;
  fGDAQ->updateDAQInfo(fDAQInfo);
  fEvIdx = fDAQ->GetCntEv();
} // end member function updateDAQInfo
